/*
 * Package il.ac.biu.cs.grossmm.test.cppToFooAdapter
 * File FooAdapter.java
 * 
 * This is a part of presence service framework API. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.tests.fooAdapters;

import il.ac.biu.cs.grossmm.api.OperationFailedException;
import il.ac.biu.cs.grossmm.api.presence.Entity;
import il.ac.biu.cs.grossmm.api.server.SelfService;
import il.ac.biu.cs.grossmm.api.server.ServiceManager;
import il.ac.biu.cs.grossmm.foo.protocol.ByeRequest;
import il.ac.biu.cs.grossmm.foo.protocol.HelloRequest;
import il.ac.biu.cs.grossmm.foo.protocol.NioServer;
import il.ac.biu.cs.grossmm.foo.protocol.Request;
import il.ac.biu.cs.grossmm.foo.protocol.Response;
import il.ac.biu.cs.grossmm.foo.protocol.ResponseCodes;
import il.ac.biu.cs.grossmm.foo.protocol.Session;
import il.ac.biu.cs.grossmm.foo.protocol.UserSession;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class FooProvider extends NioServer implements SelfService {
	Set<RequestHandler> handlers = new HashSet<RequestHandler>();

	Map<Class<? extends Request>, ArrayList<RequestHandler>> requestMap =
		new HashMap<Class<? extends Request>, ArrayList<RequestHandler>>();

	Set<AuthenticationHandler> authenticators =
		new HashSet<AuthenticationHandler>();

	public void registerRequestHandler(Class<? extends Request> rClass,
		RequestHandler handler)
	{
		try {
			rClass.getMethod("understand").invoke(null);
		} catch (Exception e) {
			e.printStackTrace();
			return; // not a request, cannot register
		}

		handlers.add(handler);

		ArrayList<RequestHandler> list = requestMap.get(rClass);

		if (list == null) {
			list = new ArrayList<RequestHandler>();
			requestMap.put(rClass, list);
		}

		if (!list.contains(handler))
			list.add(handler);
	}

	public void registerAuthenticationHandler(AuthenticationHandler handler)
	{
		HelloRequest.understand();
		authenticators.add(handler);
	}

	@Override
	protected void requestReceived(Session session, Request request)
	{
		if (request instanceof HelloRequest) {
			hello(session, (HelloRequest) request);
			return;
		}

		if (request instanceof ByeRequest) {
			bye(session, (ByeRequest) request);
			return;
		}

		List<RequestHandler> list = requestMap.get(request.getClass());

		if (list == null) {
			session.send(new Response(request, ResponseCodes.BAD_REQUEST,
				request.getCommand()));
			return;
		}
		
		// dispatch request
		
		try {
			for( RequestHandler handler : list ) {
				if( handler.requestReceived(session, request) )
					return;
			}
		} catch (OperationFailedException e) {
			e.printStackTrace();
			session.send(new Response(request, ResponseCodes.INTERNAL_ERROR, 
				e.getMessage()));
			
			return;
		}
		
		session.send(new Response(request, ResponseCodes.NOT_FOUND));
	}

	/**
	 * @param session
	 * @param request
	 */
	private void bye(Session session, ByeRequest request)
	{
		String user = request.getUser();
		UserSession u = session.getUserSession(user);

		if (u == null) {
			session.send(new Response(request, ResponseCodes.UNAUTHORIZED));
			return;
		}

		for (RequestHandler h : handlers)
			h.dettached(u);

		session.removeUserSession(user);
	}

	private void hello(Session session, HelloRequest request)
	{
		String user = request.getUser();
		String digest = request.getDigest();

		if (session.getUserSession(user) != null) {
			session.send(new Response(request,
				ResponseCodes.ALREADY_AUTHENTICATED, "User " + user
					+ " already authenticated"));
		}

		for (AuthenticationHandler auth : authenticators) {
			Entity e = auth.authenticate(user, digest);
			if (e != null) {
				UserSession u = session.creatUserSession(user, e);
				session.send(new Response(request, ResponseCodes.OK));

				for (RequestHandler h : handlers)
					h.attached(u);

				return;
			}
		}

		session.send(new Response(request, ResponseCodes.UNAUTHORIZED));
	}

	@Override
	protected void terminating(Session session)
	{
		for (RequestHandler handler : handlers)
			handler.terminating(session);
	};

	public void start(ServiceManager serviceManager) throws Exception
	{
		startListening(4004);
	}

	public void stop(Exception exception)
	{
		stopListening();
	}

	public void terminated(Object serviceHint, Exception e) throws Exception
	{
		// this service does not use other services

		throw new RuntimeException("Not implemented");
	}

}
